/**
 * 
 */
package server;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import server.connection.SelectorAdapter;
import server.connection.protocol.Event;
import server.connection.protocol.EventType;
import server.connection.sending.SendingAdapter;
import server.exception.ServerException;
import server.game.EndgameCause;
import server.log.Logging;
import server.room.Room;
import server.room.RoomManager;
import server.room.RoomState;
import server.user.User;
import server.user.UserManager;

/**
 * Event dispatcher is a module, which accepts all events incoming to the server, 
 * processes them and sends response to appropriate clients.  
 *   
 * Event dispatcher module is implemented as a standalone EventDispatcher object. 
 * 
 * @author Kamil Nowak
 * 
 */
public class EventDispatcher {

	private final static EventDispatcher instance = new EventDispatcher();
	
	private final static SendingAdapter SENDING_ADAPTER = SendingAdapter.instance();
	private final static UserManager USER_MANAGER = UserManager.instance();
	private final static RoomManager ROOM_MANAGER = RoomManager.instance();
	private final static SelectorAdapter SELECTOR_ADAPTER = SelectorAdapter.instance();
	
	private Logger logger = Logging.instance().getLogger("event_dispatcher");
	
	private final ConcurrentHashMap<Long,State> connectionStates = new ConcurrentHashMap<Long,State>(SelectorAdapter.instance().getDefaultConnsNum());
	//private final List<Long> introducedConnections = new LinkedList<Long>();
	private final ConcurrentHashMap<Long,Long> introducedConnections = new ConcurrentHashMap<Long,Long>();
	
	private static class State {
		 private StateType state;
		 private Long connectionId;		  
		 
		 State(Long connectionId) {
			 this.connectionId = connectionId;
			 state = StateType.CREATED;
		 }
		 
		 boolean moveToState(StateType newState) {
			 if (!canMoveToState(newState)) return false;
			 state = newState;
			 return true;
		 }

		public Long getConnectionId() {
			return connectionId;
		}

		public StateType getState() {
			return state;
		}

		public boolean canMoveToState(StateType newState) {
			return StateType.existsTransision(state,newState);
		}
		 
		 
	}
	
	private enum StateType {
		CREATED,
		INTRODUCED,
		LOGGEDIN;
		
		static boolean existsTransision(StateType oldState,StateType newState) {
			if (oldState == StateType.CREATED && 
					(newState == StateType.INTRODUCED)) {
				return true;
			} else if (oldState == StateType.INTRODUCED && 
					(newState == StateType.LOGGEDIN)) {
				return true;
			}
			
			return false;
		}
	}
	
	private EventDispatcher() {}
	
	/**
	 * Gets standalone instance of EventDispatcher class
	 * @return standalone object
	 */
	public static EventDispatcher instance() {
		return instance;
	}

	/**
	 * Add and process incoming event
	 * 
	 * @param event event to process
	 * @throws ServerException if event processing caused problems
	 */
	public void addEvent(Event event) throws ServerException {
		
		logger.info("Processing event: " + event.toString());
		
		switch (event.getType()) {
			case HELLO: proceedHello(event.getConnectionId()); break;
			case LOGIN: proceedLogin(event.getParams(),event.getConnectionId()); break;
			case GETTABLES: proceedGettables(event.getConnectionId()); break;
			case SMSG: proceedSMsg(event.getParams(),event.getConnectionId()); break;
			case PROMO: proceedPromo(event.getParams(),event.getConnectionId()); break;
			case PROMOOK: proceedPromoOk(event.getParams(),event.getConnectionId()); break;
			case PROMOERR: proceedPromoErr(event.getParams(),event.getConnectionId()); break;
			case CREATE: proceedCreate(event.getParams(),event.getConnectionId()); break;
			case JOIN: proceedJoin(event.getParams(),event.getConnectionId()); break;
			case LEAVETABLE: proceedLeavetable(event.getParams(),event.getConnectionId()); break;
			case MOVE: proceedMove(event.getParams(),event.getConnectionId()); break;
			case MOVEOK: proceedMoveok(event.getParams(),event.getConnectionId()); break;
			case MOVEERR: proceedMoveerr(event.getParams(),event.getConnectionId()); break;
			case FINISHGAME: proceedFinishgame(event.getParams(),event.getConnectionId()); break;
			case FINISHGAMEOK: proceedFinishgameok(event.getParams(),event.getConnectionId()); break;
			case FINISHGAMEERR: proceedFinishgameerr(event.getParams(),event.getConnectionId()); break;
			case TIMEOUT: proceedTimeout(event.getParams()); break;
			case EXIT: proceedExit(event.getConnectionId()) ; break;
			default: break;
		}
		
	}


	private void proceedPromoOk(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			if (room.getUserTurn() != connectionId) throw new ServerException(3); 
			
			synchronized (room) {
				//will be moved by returning FINISHGAMEOK or FINISHGAMEERR
				if (room.getState() != RoomState.PENDING_FINISH) room.moveToState(RoomState.GAME);
				
				SENDING_ADAPTER.send(new Event(EventType.PROMOOK, 
												new String[] { room.getId().toString()},
															   room.getOtherPlayer(connectionId)  ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			throw new ServerException(5); 
		} catch (ServerException e) {
			throw e;
		}	
		
	}

	private void proceedPromoErr(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			if (room.getUserTurn() != connectionId) throw new ServerException(3); 
			
			
			synchronized (room) {
				//will be moved by returning FINISHGAMEOK or FINISHGAMEERR
				if (room.getState() != RoomState.PENDING_FINISH) room.moveToState(RoomState.GAME);
				room.changeUserTurn();
				
				SENDING_ADAPTER.send(new Event(EventType.PROMOERR, 
												new String[] { room.getId().toString(),"44"}, 
															   room.getOtherPlayer(connectionId)  ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			throw new ServerException(5); 
		} catch (ServerException e) {
			throw e;
		}	
		
	}

	private void proceedPromo(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			
			User user = USER_MANAGER.getUser(connectionId);
			
			synchronized (room) {
				
				room.moveToState(RoomState.PROMO,connectionId);
				SENDING_ADAPTER.send(new Event(EventType.PROMO, 
												new String[] { room.getId().toString(),
															   connectionId.toString(),
															   user.getLogin(),
															   Integer.valueOf(params[3]).toString(),
															   Integer.valueOf(params[4]).toString(),
															   Integer.valueOf(params[5]).toString(),
															   Integer.valueOf(params[6]).toString(),
															   Integer.valueOf(params[7]).toString()},
															   room.getUserTurn() ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			SENDING_ADAPTER.send(new Event(EventType.PROMOERR, new String[] { "0","4" },connectionId ));  
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			SENDING_ADAPTER.send(new Event(EventType.PROMOERR, new String[] { "0","5" },connectionId ));  
			throw new ServerException(5); 
		} catch (ServerException e) {
			SENDING_ADAPTER.send(new Event(EventType.PROMOERR, new String[] { params[0],new Integer(e.getCode()).toString()  },connectionId )); 
			throw e;
		}
		
	}

	private void proceedMoveerr(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			if (room.getUserTurn() != connectionId) throw new ServerException(3); 
			
			
			synchronized (room) {
				//will be moved by returning FINISHGAMEOK or FINISHGAMEERR
				if (room.getState() != RoomState.PENDING_FINISH) room.moveToState(RoomState.GAME);
				room.changeUserTurn();
				
				SENDING_ADAPTER.send(new Event(EventType.MOVEERR, 
												new String[] { room.getId().toString(),"44"}, 
															   room.getOtherPlayer(connectionId)  ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			throw new ServerException(5); 
		} catch (ServerException e) {
			throw e;
		}	
		
	}

	private void proceedMoveok(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			if (room.getUserTurn() != connectionId) throw new ServerException(3); 
			
			synchronized (room) {
				//will be moved by returning FINISHGAMEOK or FINISHGAMEERR
				if (room.getState() != RoomState.PENDING_FINISH) room.moveToState(RoomState.GAME);
				
				SENDING_ADAPTER.send(new Event(EventType.MOVEOK, 
												new String[] { room.getId().toString()},
															   room.getOtherPlayer(connectionId)  ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			throw new ServerException(5); 
		} catch (ServerException e) {
			throw e;
		}	
	}

	private void proceedFinishgameerr(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			
			if (room.getUserTurn() != connectionId) throw new ServerException(6); 
			
			synchronized (room) {
				room.moveToState(RoomState.GAME);
				SENDING_ADAPTER.send(new Event(EventType.FINISHGAMEERR, new String[] { room.getId().toString(),"44" },room.getOtherPlayer(connectionId) )); 
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			throw new ServerException(7); 
		} catch (ServerException e) {
			throw e;
		}		
	}
	
	private void proceedFinishgameok(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			
			//Long otherPlayer = room.getUserTurn() == room.getFirstPlayer() ? room.getSecondPlayer() : room.getFirstPlayer();
			if (room.getUserTurn() != connectionId) throw new ServerException(6); 
			
			synchronized (room) {
				room.moveToState(RoomState.OCCUPIED);
				SENDING_ADAPTER.send(new Event(EventType.FINISHGAMEOK, new String[] { room.getId().toString() },room.getOtherPlayer(connectionId) ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			throw new ServerException(7); 
		} catch (ServerException e) {
			throw e;
		}
		
	}

	private void proceedExit(Long connectionId) {
		
		try {
			SELECTOR_ADAPTER.getSelectorThread(connectionId).closeConnection(connectionId);
			connectionCleanUp(connectionId);
		} catch (IOException e) {
			logger.debug(e);
		}
	}

	private void proceedTimeout(String[] params) {
		// TODO Auto-generated method stub
		
	}

	private void proceedFinishgame(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			
			EndgameCause.getCause(Integer.valueOf(params[3]));
			
			User user = USER_MANAGER.getUser(connectionId);
			
			synchronized (room) {
				room.moveToState(RoomState.PENDING_FINISH,connectionId);
				SENDING_ADAPTER.send(new Event(EventType.FINISHGAME, new String[] { room.getId().toString(),
																					connectionId.toString(),
																					user.getLogin(),
																					params[3],
																					params[4] },
																					room.getOtherPlayer(connectionId) ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			SENDING_ADAPTER.send(new Event(EventType.FINISHGAMEERR, new String[] { "0","4" },connectionId ));  
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			SENDING_ADAPTER.send(new Event(EventType.FINISHGAMEERR, new String[] { "0","5" },connectionId ));  
			throw new ServerException(7); 
		} catch (ServerException e) {
			SENDING_ADAPTER.send(new Event(EventType.FINISHGAMEERR, new String[] { params[0],new Integer(e.getCode()).toString()  },connectionId )); 
			throw e;
		}

	}

	private void proceedMove(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Room room = ROOM_MANAGER.getRoom(Long.valueOf(params[0]));
			if (!isInRoom(room,connectionId)) throw new ServerException(2); 
			
			User user = USER_MANAGER.getUser(connectionId);
			
			synchronized (room) {
				if (room.getState() == RoomState.OCCUPIED) room.moveToState(RoomState.GAME);
				room.moveToState(RoomState.MOVE,connectionId);
				SENDING_ADAPTER.send(new Event(EventType.MOVE, 
												new String[] { room.getId().toString(),
															   connectionId.toString(),
															   user.getLogin(),
															   Integer.valueOf(params[3]).toString(),
															   Integer.valueOf(params[4]).toString(),
															   Integer.valueOf(params[5]).toString(),
															   Integer.valueOf(params[6]).toString() },
															   room.getUserTurn() ));
			}
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			SENDING_ADAPTER.send(new Event(EventType.MOVEERR, new String[] { "0","4" },connectionId ));  
			throw new ServerException(4); 
		} catch (NumberFormatException e) {
			SENDING_ADAPTER.send(new Event(EventType.MOVEERR, new String[] { "0","5" },connectionId ));  
			throw new ServerException(5); 
		} catch (ServerException e) {
			SENDING_ADAPTER.send(new Event(EventType.MOVEERR, new String[] { params[0],new Integer(e.getCode()).toString() },connectionId )); 
			throw e;
		}
	}

	private void proceedLeavetable(String[] params, Long connectionId) throws ServerException {
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Long roomId = Long.valueOf(params[0]);
			User user = USER_MANAGER.getUser(connectionId);
			
			if (ROOM_MANAGER.leaveRoom(roomId,connectionId)) user.removeRoom(roomId);
			
			//broadcastRoomsState();
			
		} catch (ArrayIndexOutOfBoundsException e) {
			//SENDING_ADAPTER.send(new Event(EventType.JOINERR, new String[] { "33" },connectionId ));
			throw new ServerException(8); 
		} catch (NumberFormatException e) {
			//SENDING_ADAPTER.send(new Event(EventType.JOINERR, new String[] { "33" },connectionId ));
			throw new ServerException(7); 
		} catch (ServerException e) {
			//SENDING_ADAPTER.send(new Event(EventType.JOINERR, new String[] { "55" },connectionId ));
			throw e;
		}
		
	}

	private void proceedJoin(String[] params, Long connectionId) throws ServerException {
		
		try {
			
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Long roomId = Long.valueOf(params[0]);
			
			ROOM_MANAGER.joinRoom(Long.valueOf(params[0]),connectionId);
			User user = USER_MANAGER.getUser(connectionId);
			synchronized (user) {
				user.addRoom(roomId);
			}
			Room room = ROOM_MANAGER.getRoom(roomId);
			
			SENDING_ADAPTER.send(new Event(EventType.JOINOK, new String[] { params[0] },connectionId ));
			SENDING_ADAPTER.send(new Event(EventType.JOIN, new String[] { params[0],connectionId.toString(),user.getLogin() },room.getOtherPlayer(connectionId)));
			
			broadcastRoomsState();
			
		} catch (ArrayIndexOutOfBoundsException e) {
			SENDING_ADAPTER.send(new Event(EventType.JOINERR, new String[] { "0","4" },connectionId ));  
			throw new ServerException(8); 
			
		} catch (NumberFormatException e) {
			SENDING_ADAPTER.send(new Event(EventType.JOINERR, new String[] { "0","5" },connectionId )); 
			throw new ServerException(7); 
		} catch (ServerException e) {
			SENDING_ADAPTER.send(new Event(EventType.JOINERR, new String[] { params[0], new Integer(e.getCode()).toString()  },connectionId )); 
			throw e;
		}
		
	}

	private void proceedCreate(String[] params, Long connectionId) throws ServerException {
		
		try {
			
			if (!isLoggedIn(connectionId)) throw new ServerException(1);
			
			Long roomId = ROOM_MANAGER.addRoom(params[2],connectionId);
			
			User user = USER_MANAGER.getUser(connectionId);
			synchronized (user) {
				user.addRoom(roomId);
			}
			
			SENDING_ADAPTER.send(new Event(EventType.CREATEOK, new String[] { roomId.toString() },connectionId ));
			
			broadcastRoomsState();
			
		} catch (ArrayIndexOutOfBoundsException e) {
			SENDING_ADAPTER.send(new Event(EventType.CREATEERR, new String[] { "15" },connectionId )); 
			throw new ServerException(15); 
		} catch (ServerException e) {
			SENDING_ADAPTER.send(new Event(EventType.CREATEERR, new String[] { new Integer(e.getCode()).toString() },connectionId )); 
			throw e;
		}
		
	}

	private void proceedSMsg(String[] params, Long connectionId) throws ServerException {
		
		try {
			
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			
			Long roomId = Long.valueOf(params[0]);
			
			String[] sendParams = new String[] { roomId.toString(),
												 connectionId.toString(),
												 USER_MANAGER.getUser(connectionId).getLogin(),
												 params[3]};
			
			Event event = new Event(EventType.MSG,sendParams);
			
			if (roomId == 0) {
				SENDING_ADAPTER.sendAll(event);
			} else {
				Room room = ROOM_MANAGER.getRoom(roomId);
				if (room == null) new ServerException(9); 
				if (connectionId != room.getFirstPlayer() && connectionId != room.getSecondPlayer()) new ServerException(2); 
				
				event.setConnectionId(room.getFirstPlayer());
				SENDING_ADAPTER.send(event);
				if (room.getSecondPlayer() != null)
					SENDING_ADAPTER.send(new Event( EventType.MSG,sendParams,room.getSecondPlayer() ));
			}
			
		} catch (ArrayIndexOutOfBoundsException e) {
			throw new ServerException(11); 
		} catch (NumberFormatException e) {
			throw new ServerException(7); 
		} 
		
	}

	private void proceedGettables(Long connectionId) throws ServerException {
		
		try {
			if (!isLoggedIn(connectionId)) throw new ServerException(1); 
			SENDING_ADAPTER.send(new Event(EventType.TABLES, prepareTablesParams(),connectionId ));
			
		} catch (ServerException e) {
			throw e;
		}
		
	}

	private void proceedHello(Long connectionId) throws ServerException {
		
		State connection = connectionStates.get(connectionId);
		if (connection == null) {
			connection = new State(connectionId);
			connectionStates.put(connectionId,connection);
		}
		if (connection.moveToState(StateType.INTRODUCED))
			SENDING_ADAPTER.send(new Event(EventType.OK, null,connectionId ));
		else { 
			SENDING_ADAPTER.send(new Event(EventType.ERR, new String[] { "12" },connectionId ));
			throw new ServerException(12); 
		}
	}

	private void proceedLogin(String[] params, Long connectionId) throws ServerException {
		
		try {
			State connection = connectionStates.get(connectionId);
			if ( connection == null || !connection.canMoveToState(StateType.LOGGEDIN) ) throw new ServerException(13); 
			
			USER_MANAGER.addUser(params[0],connectionId);
			
			if ( !connection.moveToState(StateType.LOGGEDIN) ) throw new ServerException(13); 
			SENDING_ADAPTER.send(new Event(EventType.LOGINOK, new String[] { connectionId.toString() },connectionId ));
			
			SENDING_ADAPTER.send(new Event(EventType.TABLES, prepareTablesParams(),connectionId ));
			
			synchronized (introducedConnections) {
				introducedConnections.put(connectionId,connectionId);
			}
			
			
			
		} catch (ArrayIndexOutOfBoundsException e) {
			SENDING_ADAPTER.send(new Event(EventType.LOGINERR, new String[] { "14" },connectionId )); 
			throw new ServerException(14); 
		} catch (ServerException e) {
			SENDING_ADAPTER.send(new Event(EventType.LOGINERR, new String[] { new Integer(e.getCode()).toString() },connectionId )); 
			throw e;
		}
		
	}

	/**
	 * Removes all client data on the logic side (EventDispatcher, UserManager, RoomManager)  
	 * If necessary performs actions in order to inform other clients about disconnection 
	 * 
	 * @param connectionId connectionId to clean
	 */
	public void connectionCleanUp(Long connectionId) {
		
		//removing connection
		connectionStates.remove(connectionId);
		introducedConnections.remove(connectionId);
		SELECTOR_ADAPTER.removeConnection(connectionId);
		
		//removing user
		User user = USER_MANAGER.getUser(connectionId);
		if (user != null) {
			USER_MANAGER.removeUser(connectionId);
			//boolean broadcastTables = !user.getRooms().isEmpty();
			synchronized (user) {
				Iterator<Long> rooms = user.getRooms().iterator();
				while (rooms.hasNext()) {
					Long roomId = rooms.next();
					try {
						if(ROOM_MANAGER.leaveRoom(roomId,connectionId)) rooms.remove();
					} catch (ServerException e) {
						logger.debug("connectionCleanUp: ",e); 
					}
				}	
			}
			//if (broadcastTables) broadcastRoomsState();
		}
	}
	
	/**
	 * Returns all connections having been introduced to the server (said HELLO)
	 * 
	 * @return collection of connections
	 */
	public LinkedList<Long> getIntroducedConnections() {
		synchronized (introducedConnections) {
			//return new LinkedList<Long>(introducedConnections);
			return new LinkedList<Long>(introducedConnections.values());
		}
	}
	
	private boolean isLoggedIn(Long connectionId) {
		State connection = connectionStates.get(connectionId);
		return connection != null && connection.getState() == StateType.LOGGEDIN;
	}
	
	private String[] prepareTablesParams() {
		Enumeration<Room> rooms = ROOM_MANAGER.getRooms();
		Vector<String> params = new Vector<String>();
		
		while (rooms.hasMoreElements()) {
			Room room = rooms.nextElement();
			params.add(room.getName());
			params.add(room.getFirstPlayer().toString());
			params.add((USER_MANAGER.getUser(room.getFirstPlayer())).getLogin());
			params.add(room.getId().toString());
			if (room.getState() == RoomState.FREE) params.add("F"); 
			else params.add("R"); 
		}
		return params.toArray(new String[params.size()]);
	}
	
	
	private void broadcastRoomsState() {
		SENDING_ADAPTER.sendAll(new Event(EventType.TABLES, prepareTablesParams()));
	}
	
	private boolean isInRoom(Room room, Long connectionId) {
		return room != null && (room.getFirstPlayer() == connectionId || room.getSecondPlayer() == connectionId);
	}
}
